package etcdclient

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/pkg/errors"
	"go.etcd.io/etcd/api/v3/mvccpb"
	"go.etcd.io/etcd/client/v3"
	"time"
)

type EtcdCient struct {
	client *clientv3.Client
}

func NewClient(ctx context.Context, conn []string, username string, password string, timeout time.Duration) (*EtcdCient, error) {
	config := clientv3.Config{
		Endpoints:   conn,    // 节点信息
		DialTimeout: timeout, // 超时时间
		Username:    username,
		Password:    password,
	}

	if client, err := clientv3.New(config); err != nil {
		g.Log().Error(ctx, err.Error())
		return nil, err
	} else {

		return &EtcdCient{
			client: client,
		}, nil
	}
}

// Get 获取指定key的value
func (t *EtcdCient) Get(ctx context.Context, key string) ([]byte, error) {
	kv := clientv3.NewKV(t.client)
	if getResp, err := kv.Get(ctx, key); err != nil {
		return nil, err
	} else {
		return getResp.Kvs[0].Value, nil
	}
}

// GetPrefix 获取指定前缀的key对应的map
func (t *EtcdCient) GetPrefix(ctx context.Context, key string) (map[string][]byte, error) {
	kv := clientv3.NewKV(t.client)
	if getResp, err := kv.Get(ctx, key, clientv3.WithPrefix()); err != nil {
		return nil, err
	} else {

		if len(getResp.Kvs) == 0 {
			return nil, errors.New(" no data")
		}

		var data = make(map[string][]byte, 0)
		for _, v := range getResp.Kvs {
			mapkey := string(v.Key)
			data[mapkey] = v.Value
		}

		return data, nil
	}
}

func (t *EtcdCient) Del(ctx context.Context, key string) error {
	kv := clientv3.NewKV(t.client)
	if delResp, err := kv.Delete(context.TODO(), key); err != nil {
		return err
	} else {

		if len(delResp.PrevKvs) > 0 {
			for _, kvpair := range delResp.PrevKvs {
				g.Log().Info(ctx, "del data:", string(kvpair.Key), string(kvpair.Value))
			}
		}
	}

	return nil
}

func (t *EtcdCient) DelWithPrefix(ctx context.Context, key string) error {
	kv := clientv3.NewKV(t.client)
	if delResp, err := kv.Delete(context.TODO(), key, clientv3.WithPrefix()); err != nil {
		return err
	} else {

		if len(delResp.PrevKvs) > 0 {
			for _, kvpair := range delResp.PrevKvs {
				g.Log().Info(ctx, "del data:", string(kvpair.Key), string(kvpair.Value))
			}
		}
	}

	return nil
}

// Adddata 永不过期的kv
func (t *EtcdCient) Adddata(ctx context.Context, key string, value string) error {
	kv := clientv3.NewKV(t.client)
	_, err := kv.Put(ctx, key, value)
	if err != nil {
		return err
	}

	return nil
}

// AdddataWithLease 添加租约的key/value
func (t *EtcdCient) AdddataWithLease(ctx context.Context, key string, value string, ttl int64) error {
	lesase, err := t.client.Grant(ctx, ttl)
	if err != nil {
		return err
	}

	leaseId := lesase.ID

	// 获得kv api子集
	kv := clientv3.NewKV(t.client)

	// put一个kv，让它与租约关联起来
	if putResp, err := kv.Put(context.TODO(), key, value, clientv3.WithLease(leaseId)); err != nil {
		g.Log().Error(ctx, err.Error())
		return err
	} else {
		g.Log().Info(ctx, "写入成功", putResp.Header.Revision)
		return nil
	}
}

func (t *EtcdCient) GetClient() *clientv3.Client {
	return t.client
}

func (t *EtcdCient) Stop() error {
	return t.client.Close()
}

func (t *EtcdCient) WatchWithDeal(ctx context.Context, keyprefix string, f func(ctx1 context.Context, data []byte, datatype int)) {
	// 创建一个监听器
	watcher := clientv3.NewWatcher(t.client)
	// 启动监听 5秒后关闭 , 永久监听
	//ctx, cancelFunc := context.WithCancel(context.TODO())
	//time.AfterFunc(5*time.Second, func() {
	//	cancelFunc()
	//})
	watchRespChan := watcher.Watch(ctx, keyprefix, clientv3.WithPrefix())
	// 处理kv变化事件
	for watchResp := range watchRespChan {
		for _, event := range watchResp.Events {
			switch event.Type {
			case mvccpb.PUT:
				if event.IsCreate() {
					f(ctx, event.Kv.Value, 0)
				} else {
					f(ctx, event.Kv.Value, 1)
				}

			case mvccpb.DELETE:
				f(ctx, event.Kv.Key, 2)
			}
		}
	}
}
